'use strict'

import { fileIo as fs } from '@ohos.file.fs';
import { path } from '@ohos/node-polyfill';
import { SourceMapConsumer, SourceMapGenerator } from 'source-map-js';

function fromBase64(str) {
    if (Buffer) {
        return Buffer.from(str, 'base64').toString()
    } else {
        /* c8 ignore next 2 */
        return window.atob(str)
    }
}

class PreviousMap {
    constructor(css, opts) {
        if (opts.map === false) {
            return
        }
        this.loadAnnotation(css)
        this.inline = this.startWith(this.annotation, 'data:')

        let prev = opts.map ? opts.map.prev : undefined
        let text = this.loadMap(opts.from, prev)
        if (!this.mapFile && opts.from) {
            this.mapFile = opts.from
        }
        if (this.mapFile) {
            this.root = path.dirname(this.mapFile)
        }
        if (text) {
            this.text = text
        }
    }

    consumer() {
        if (!this.consumerCache) {
            this.consumerCache = new SourceMapConsumer(this.text)
        }
        return this.consumerCache
    }

    decodeInline(text) {
        let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/
        let baseUri = /^data:application\/json;base64,/
        let charsetUri = /^data:application\/json;charset=utf-?8,/
        let uri = /^data:application\/json,/

        let uriMatch = text.match(charsetUri) || text.match(uri)
        if (uriMatch) {
            return decodeURIComponent(text.substr(uriMatch[0].length))
        }

        let baseUriMatch = text.match(baseCharsetUri) || text.match(baseUri)
        if (baseUriMatch) {
            return fromBase64(text.substr(baseUriMatch[0].length))
        }

        let encoding = text.match(/data:application\/json;([^,]+),/)[1]
        throw new Error('Unsupported source map encoding ' + encoding)
    }

    getAnnotationURL(sourceMapString) {
        return sourceMapString.replace(/^\/\*\s*# sourceMappingURL=/, '').trim()
    }

    isMap(map) {
        if (typeof map !== 'object') {
            return false
        }
        return (
            typeof map.mappings === 'string' ||
                typeof map._mappings === 'string' ||
            Array.isArray(map.sections)
        )
    }

    loadAnnotation(css) {
        let comments = css.match(/\/\*\s*# sourceMappingURL=/g)
        if (!comments) {
            return
        }

        // sourceMappingURLs from comments, strings, etc.
        let start = css.lastIndexOf(comments.pop())
        let end = css.indexOf('*/', start)

        if (start > -1 && end > -1) {
            // Locate the last sourceMappingURL to avoid pickin
            this.annotation = this.getAnnotationURL(css.substring(start, end))
        }
    }

    loadFile(path) {
        this.root = path.dirname(path)
        if (fs.accessSync(path)) {
            this.mapFile = path

            let readTextOptions = {
                offset: 0,
                length: 0,
                encoding: 'utf-8'
            };
            let stat = fs.statSync(path);
            readTextOptions.length = stat.size;
            let str = fs.readTextSync(path, readTextOptions);
            console.info("readText succeed:" + str);
            return str
            // return readFileSync(path, 'utf-8').toString().trim()
        }
    }

    loadMap(file, prev) {
        if (prev === false) {
            return false
        }

        if (prev) {
            if (typeof prev === 'string') {
                return prev
            } else if (typeof prev === 'function') {
                let prevPath = prev(file)
                if (prevPath) {
                    let map = this.loadFile(prevPath)
                    if (!map) {
                        throw new Error(
                            'Unable to load previous source map: ' + prevPath.toString()
                        )
                    }
                    return map
                }
            } else if (prev instanceof SourceMapConsumer) {
                return SourceMapGenerator.fromSourceMap(prev).toString()
            } else if (prev instanceof SourceMapGenerator) {
                return prev.toString()
            } else if (this.isMap(prev)) {
                return JSON.stringify(prev)
            } else {
                throw new Error(
                    'Unsupported previous source map format: ' + prev.toString()
                )
            }
        } else if (this.inline) {
            return this.decodeInline(this.annotation)
        } else if (this.annotation) {
            let map = this.annotation
            if (file) {
                map = path.join(path.dirname(file), map)
            }
            return this.loadFile(map)
        }
    }

    startWith(string, start) {
        if (!string) {
            return false
        }
        return string.substr(0, start.length) === start
    }

    withContent() {
        return !!(
            this.consumer().sourcesContent &&
                this.consumer().sourcesContent.length > 0
        )
    }
}

export { PreviousMap }

export default PreviousMap
